#!/usr/sbin/rsct/perl5/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1999,2002 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# "@(#)99   1.32   src/rsct/registry/cli/bin/mksrcol.perl, srcli, rsct_rpyxh, rpyxht1f3 2/22/01 16:25:30"
######################################################################
#                                                                    #
# Module: mksrcol                                                    #
#                                                                    #
# Purpose:                                                           #
#   mksrcol - Make (add) columns to a System Registry table.         #
#                                                                    #
# Syntax:                                                            #
#   To add columns to a table using data entered on the command line:#
#       mksrcol [-h][-TV] Table col=Name type=Data_type              #
#           [def=Default_value] ...                                  #
#                                                                    #
#   To add columns to a table using data predefined in an input file:#
#       mksrcol [-h][-TV]-f Table_definition_file Table              #
#                                                                    #
# Flags:                                                             #
#   -h Help. Writes this command's usage statement to stdout.        #
#   -f Table_definition_file                                         #
#       Instead of command line data, use an input file              #
#       containing names of tables to be altered including           #
#       descriptions of columns to be added. Format of the           #
#       Table_definition_file is listed in the man page for mksrcol. #
#   -T Trace. Writes this command's trace messages to stderr.        #
#   -V Verbose. Writes this command's verbose messages to stderr.    #
#                                                                    #
# Operands:                                                          #
#   Table   The name of the table you wish to add columns to. The    #
#           Table operand can contain a relative or absolute path    #
#           name.                                                    #
#   col=Name The name of the column to be added. Must be a valid     #
#           alphanumeric string. Recommend column names follow the OO#
#           naming styles of WordWord (no underbars.)                #
#           example: NodeNumber, not node_number.                    #
#   type=Data_type Data type to be stored in the column. Valid types #
#           are listed under Description.                            #
#   def=Default_value  Optional. Default value for the column. This  #
#           value is treated by the System Registry to be of the data#
#           type given for the column.                               #
#                                                                    #
# Description:                                                       #
#   The mksrcol command allows you to add one or more columns to a   #
#   specified existing Table in the System Registry. Column          #
#   definitions include the column name, data type, qualifier,       #
#   properties and default value that define the column metadata.    #
#   Definitions are defined either on the command line or via an     #
#   input file (using -f flag.)                                      #
#                                                                    #
#   Column names must be unique to a table. Valid column names start #
#   with a letter and can contain alphanumeric characters and        #
#   underscores. It is suggested that OO naming conventions be       #
#   followed, however. (ie, using EthernetAdapter instead of         #
#   Ethernet_Adapter.) The table will automatically have a system-   #
#   generated column named RowChangeCounter. This name can not be    #
#   used for any other column.                                       #
#                                                                    #
#   Multiple column definitions can be provided through the command  #
#   line. This command is atomic in that it is guaranteed that all   #
#   of the columns will be added. In the case of an error, the table #
#   will remain unchanged.                                           #
#                                                                    #
#   Valid qualifiers are: pk|PK - Primary key, st|ST Standard column #
#                                                                    #
#   Valid data types are: i|I = 32 bit int, ui|UI = unsigned 32 bit  #
#   int, l|L 64 bit int (long), ul|UL unsigned 64 bit int,           #
#   f|F float, d|D double, s|S string, rh|RH resource handle,        #
#   b|B binary.                                                      #
#                                                                    #
#   Qualifiers and data types can be entered in upper or lower case. #
#                                                                    #
#   Resource handles are entered as hexadecimal, using the format    #
#       (including quotes):                                          #
#       "0x######## 0x######## 0x######## 0x######## 0x########"     #
#   where '########' is a hexadecimal number. If more than 5         #
#   numbers are given, only the first 5 will be used. If less than   #
#   5 are given, 0x00000000 will be placed in the missing places.    #
#                                                                    #
#   Binary data is entered as hexadecimal, using the format          #
#       (including quotes):                                          #
#       "0x######## 0x######## 0x######## ...."    -OR-              #
#       "0x################...."                                     #
#   where '########' is a hexadecimal number. The length of the      #
#   hexadecimal string is not significant (each number could be      #
#   more or less than 8 digits). Values are stored as                #
#   given in the System Registry (the leading 0x is stripped off     #
#   for storage.)                                                    #
#                                                                    #
#   Long and unsigned long (64 bit) are expected in decimal format.  #
#   It is up to the caller to make sure the column value matches the #
#   data type.                                                       #
#                                                                    #
#   Strings with spaces need to be enclosed in quotes.               #
#                                                                    #
#   For more information on entering the data types, please refer to #
#   the man page for mksrcol.                                        #
#                                                                    #
#   Input file format:                                               #
#                                                                    #
#   Column definition data (metadata) for a colums being added to an #
#   existing table can be entered using a text file with a stanza    #
#   headed by the keyword 'ColumnDefinition':                        #
#       ColumnDefinition::                                           #
#       column 3:                                                    #
#           col  = <name>                                            #
#           type = <data_type>                                       #
#           qual = <qualifier>                                       #
#           def  = <default_value>                                   #
#       column 4:                                                    #
#           col  = <name>                                            #
#           type = <data_type>                                       #
#           qual = <qualifier>                                       #
#           sd_defn = <name>_SDDefinition                            #
#           def  = <default_value>                                   #
#           ...                                                      #
#                                                                    #
#       <name>_SDDefinition::                                        #
#       element 0:                                                   #
#           name = <element name>                                    #
#           type = <data_type>                                       #
#           def  = <default_value>                                   #
#       element 1:                                                   #
#           ...                                                      #
#                                                                    #
#   The tab at the beginning of each line is provided here for       #
#   readability but is not necessary in the input file. Only the     #
#   first stanza headed 'ColumnDefinition' will be read from the     #
#   input file by this program.                                      #
#                                                                    #
#   The <name>_SDDefinition stanza declares a structured data        #
#   definition for a column using structured data or structured      #
#   data arrays.                                                     #
#                                                                    #
#   If a ColumnDefinition stanza contains a 'sd_defn=' line, then    #
#   it will be assumed there is a corresponding SDDefinition stanza  #
#   in the input file.                                               #
#                                                                    #
#   Default values given in SDDefinition stanzas are overridden by   #
#   values given in the TableDefinition stanza.                      #
#                                                                    #
# Exit Values:                                                       #
#   0  SR_CLI_SUCCESS        Command completed successfully.         #
#   1  SR_CLI_REGISTRY_ERROR Command terminated due to an underlying #
#                            System Registry error.                  #
#   2  SR_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  SR_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   4  SR_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   5  SR_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            table to be modified.                   #
#                                                                    #
# Examples:                                                          #
#   mksrcol -f myfile /temp/table1                                   #
#   - This example makes columns listed in the file 'myfile' in the  #
#   current directory to the table 'table1' in the directory /temp   #
#   of the system registry.                                          #
#                                                                    #
#   mksrcol /temp/Table3 column_name=c1 data_type=I qualifier=ST     #
#       default_value=32 column_name=c2 data_type=S qualifier=ST     #
#       default_value="George" column_name=c3 data_type=I            #
#       qualifier=ST default_value=777                               #
#   - This example makes columns to the table Table3 that is at the  #
#   root level of the system registry                                #
#                                                                    #
#   mksrcol /samples/Table4 col=c2 type=RH                           #
#       def="0xffffffff 0x12345678 0x00000000 0x00000000 0x00000000" #
#   col=c3 type=L def=9233456798234 col=b4 type=B def="0xffff"       #
#   - makes columns to the table /samples 'Table4' using RH and L    #
#   data types                                                       #
#   Note in this example, the default value for the RH typd column   #
#   was put on a separate line for readability. For the call all the #
#   data should be on one line.                                      #
#                                                                    #
#   mksrcol /samples/Host col=HostName type=S qual=PK col=HostIP     #
#      type=S col=HostID type=I default="0"                          #
#   - This example makes columns to the table /samples/Host using    #
#   varying input formats for each column. (ie, no qualifier,        #
#   no default value given.)                                         #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#                                                                    #
# Outputs:                                                           #
#   stdout - messages during execution and Verbose output            #
#   stderr - error messages                                          #
#                                                                    #
# External Ref:                                                      #
#   Commands: $LSMSG                                                 #
#   Extensions:  SR.pm                                               #
#   Perl library routines: Getopts::Std                              #
#   SR cli routines: : SR_cli_utils.pm - init_session error_exit     #
#              set_session_variables clean_session printCEMsg        #
#         SR_cli_column_utils.pm - parse_input_file, make_col_struct #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and  :%!expand -4)                           #
#                                                                    #
# Change Activity:                                                   #
#   000929 HGJ 38317: Initial delivery.                              #
#                                                                    #
######################################################################

#--------------------------------------------------------------------#
#                                                                    #
# General Program Flow/Logic:                                        #
#                                                                    #
# A: Parse command line - get table name and input file              #
# B: Initialise session with registry, including changing the        #
#    current directory if a relative path name is given (use value   #
#    given in CT_SR_HOME)                                            #
# C: Parse input file, if necessary. Create column data to be fed    #
#    into CT::SR::add_columns                                        #
# D: Open file to be edited - exit if it won't open                  #
# E: Call CT::SR::add_columns                                        #
# F: Close session table and tree                                    #
#                                                                    #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# Included libraries and extensions                                  #
#--------------------------------------------------------------------#
use lib "/usr/sbin/rsct/pm";
use locale;
use Getopt::Std;

use CT_cli_utils qw(printIMsg
                    printEMsg
);

use CT::SRrc;
use CT::SR;
use SR_cli_utils qw(init_session 
                    printCEMsg 
                    $DEFAULT_GLOBAL_MOUNT_POINT
                    open_table
                    set_session_variables
                    clean_session
                    error_exit
); 
use SR_cli_column_utils qw( make_col_struct 
                            grab_file_data
                            grab_cmdline_data
);
use SR_cli_rc qw(:return_codes);


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
# TODO: Many verbose statements in this code will eventually by
# Trace statements when the facility is available as a Perl CLI
# (feature 48401) Trace facility is limited at this time.
# TODO: security on access to the table can not be further defined
# until after a security design has been implemented for the SR.
# ( feature 48402 ) Until then, the table is opened with the
# minimum security necessary to complete the command.


# Constants
$TRUE           = 1;
$FALSE          = 0;

# Variables to remap opt_x variables produced by getopts
$Opt_File_Input = $FALSE;
$Verbose        = $FALSE;
$Trace          = $FALSE;

# Messaging variables
$PROGNAME       = "mksrcol";            # Program Name for messages
$MSGCAT         = "srcli.cat";          # msg catalogue for this cmd
$CTDIR          = "/usr/sbin/rsct";     # Cluster directory path
$CTBINDIR       = "$CTDIR/bin";         # Cluster Bin directory path
$LSMSG          = "$CTBINDIR/ctdspmsg"; # Display message routine
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps";  # Msg maps used by $LSMSG

%Cleanup = ();                          # Hash of items to cleanup
                                        # {Session} $session to term

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
# Variables for use with extensions
my $Table_handle   = ""; 
my $Tree_handle    = "";

# Initialised to CT::SR::column_struct_t class in make_col_struct
my $Column_data    = "";

# Flags use of CT::SR::change_directory
my $Set_work_dir   = $FALSE;        
my $filename       = "";
my $Table          = "";
my $Table_name     = "";
my $rc             = 0;                 # assume good return code

my $Mount_point    = $DEFAULT_GLOBAL_MOUNT_POINT;



#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
# Parse the command line, exit if there are errors
# Error messages handled in subroutine
($rc, $Table_name, $filename) = parse_cmd_line();  
($rc == 0) || error_exit($rc);

if ($Verbose) {
    printIMsg("IMsgmksrcolCommandLineInput", $filename, $Table_name);
}

# Initialize Registry Library Connection
# Error messages handled in subroutine
($Set_work_dir, $Table) = set_session_variables($Table_name);
($rc,$Tree_handle) = init_session($Set_work_dir);
($rc == 0) || error_exit($rc);

# Flag that the session was set up, so will need to be cleaned up
$Cleanup{Session} = $Tree_handle;

# Process input depending on whether an input file was provided
# or not.
if ($Opt_File_Input) {
    ($rc, $Column_data) = grab_file_data($filename, "ColumnDefinition");
} 
else {
    ($rc, $Column_data) = grab_cmdline_data(@ARGV);
}

# Check error code
($rc == 0) || error_exit($rc);


($rc, @Column_ptr) = make_col_struct($Column_data);
($rc == 0) || error_exit($rc);


$Verbose && 
    printIMsg("IMsgmksrcolMakingColumns", $Table_name, $Table);

($rc, $Table_handle) = open_table($Tree_handle, $Table, 
                        $Table_name, SR_METADATA);
($rc == 0) || error_exit($rc);

                
# Call to C API to make columns. Report on success or failure.
$Trace && print STDERR "Calling CT::SR::add_columns\n";
$rc = CT::SR::add_columns($Table_handle, \@Column_ptr);
$Trace && print STDERR "CT::SR::add_columns return code: $rc\n";

$rc = error_check("sr_add_columns", $rc, $Table_name);
($rc == 0) || error_exit($rc);


# Even if CT::SR::add_columns failed, clean up session
$rc = clean_session($Tree_handle, $Mount_point, $Table_handle);

# Making this hash be empty will allow error_exit to check
# the final return code
%Cleanup = ();
($rc == 0 ) || error_exit($rc);

# If the code gets to here, then the return code is 0
exit $rc;

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# parse_cmd_line:                                                    #
#   Uses getopts() to grab flags on the command line, including an   #
#   input file name if the '-f' flag is used.                        #
#                                                                    #
# Return values:                                                     #
#   $local_rc - local return code                                    #
#   $table_name - table name to be made                              #
#   $filename - input filename, if applicable                        #
#                                                                    #
# Global variables modified:                                         #
#   $Opt_File_Input    output   True (-f) file input.                #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#--------------------------------------------------------------------#
sub parse_cmd_line
{
$local_rc = 0;
my %opts = ();

if (getopts('f:hTV', \%opts) == 0) {      # parse input flags
    # Return Error parsing flags - add a message here, too.
    printCEMsg("EMsgSRcliInvalidFlag");
    print_usage();
    return SR_CLI_BAD_FLAG;
}                  

if (defined $opts{h}) {              # print usage and exit
    print_usage();
    exit(0);                         # exit quickly
}

# Get table name from input.
my $table_name = shift @ARGV;
if ((!$table_name ) || ($table_name =~ /=/)) {
    printCEMsg("EMsgSRcliNoTableName");
    print_usage();
    return SR_CLI_BAD_OPERAND;
}

if (defined $opts{f}) {
    $Opt_File_Input = $TRUE;
    $filename = $opts{f};
    if ($#ARGV >= 0) {
        printCEMsg("EMsgSRcliTooManyOperands");
        print_usage();
        return SR_CLI_BAD_OPERAND;
    }
}

if (!defined $opts{f} && $#ARGV <= 0) {
    printCEMsg("EMsgSRcliNoColumnData");
    print_usage();
    return SR_CLI_BAD_OPERAND;
}

# Set Trace flag if requested
if (defined $opts{T}) {
    $Trace = $TRUE;
}

# Set Verbose flag if requested
if (defined $opts{V}) {
    $Verbose = $TRUE;
}

return ($local_rc, $table_name, $filename);
};  # end parse_cmd_line


#--------------------------------------------------------------------#
# error_check:                                                       #
#   Checks the return code from the SR function.  If an error is     #
#   detected appropriate error messages will be displayed and        #
#   SR CLI return code set.                                          #
#                                                                    #
# Parameters:                                                        #
#   $sr_function  - Name of the SR function that was called and      #
#                   whose error code we are checking.                #
#   $sr_rc        - SR function return code.                         #
#   $table_name   - Name of the table trying to create.              #
#                                                                    #
# Return values:                                                     #
#   None.                                                            #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($sr_function, $sr_rc, $table_name) = @_; 
my $rc = 0;

# Check the return code
if ($sr_rc != 0) {
    if ($sr_rc == SR_NO_PERMISSION) {
        printEMsg("EMsgmksrcolNoPermission", $table_name);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_NO_DIRECTORY) {
        printCEMsg("EMsgSRcliNoDirectory", $table_name);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_NO_TABLE) {
        printCEMsg("EMsgSRcliNoTable", $table_name);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_DUPLICATE_KEY) {
        printEMsg("EMsgmksrcolDuplicateKey");
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_DUPLICATE_COLUMN) {
        printEMsg("EMsgmksrcolDuplicateColumn");
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_INVALID_COLUMN) {
        printCEMsg("EMsgSRcliInvalidColumnData", $sr_function);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_rc == SR_CONNECTION_LOST) {
        printCEMsg("EMsgSRcliConnectionLost");
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    else {
        printEMsg("EMsgmksrcolErrorMakingColumns", $table_name);
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR;
    }
}

return $rc;
}   # end error_check


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
printIMsg("IMsgmksrcolUsage");
} # end print_usage


#--------------------------------------------------------------------#
# End File                                                           #
#--------------------------------------------------------------------#
